package fsnotifyutil

import (
	"github.com/fsnotify/fsnotify"
	"log"
	"sync"
)

type WatchFileCallback func(string) error
type WatchFile struct {
	f        string
	callback func(string) error
}

func (c *WatchFile) GetFileName() string {
	if c != nil {
		return c.f
	}
	return ""
}
func (c *WatchFile) GetCallback() func(string) error {
	if c != nil && c.callback != nil {
		return c.callback
	}
	return nil
}

type WatchManager struct {
	sync.Mutex
	watcher    *fsnotify.Watcher
	watchTable map[string]*WatchFile // 监控文件表
	closeOnce  sync.Once
	exitChan   chan struct{}
}

// NewWatcher 创建监控管理器
func NewWatchManager() (*WatchManager, error) {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, err
	}

	wm := &WatchManager{watcher: watcher,
		exitChan:   make(chan struct{}),
		watchTable: make(map[string]*WatchFile),
	}

	// 启动事件、添加、关闭的goroutine
	go wm.eventLoop()

	return wm, nil
}

// eventLoop 等待文件监控事情到来
func (c *WatchManager) eventLoop() {
	defer func() {
		log.Printf("DEBUG: fsnotifyutil: eventLoop stop.\n")
		if err := recover(); err != nil {
			log.Printf("Error: fsnotifyutil: eventLoop: err:%v\n", err)
		}
	}()

	for {
		select {
		case event := <-c.watcher.Events:
			if event.Op&fsnotify.Write == fsnotify.Write {
				log.Printf("DEBUG: eventLoop: file:%v modified\n", event.Name)
				if wf, ok := c.getWatchFile(event.Name); ok {
					cb := wf.GetCallback()
					if err := cb(event.Name); err != nil {
						log.Printf("Error: fsnotify: callback: err:%v\n", err)
					}
				}
			}

			// 重新监控
			if event.Op&fsnotify.Rename == fsnotify.Rename || event.Op&fsnotify.Remove == fsnotify.Remove {
				log.Printf("DEBUG: eventLoop: event:%v\n", event)
				if err := c.watcher.Add(event.Name); err != nil {
					log.Printf("Error: eventLoop: remonitor err:%v\n", err)
				}
			}

		case err := <-c.watcher.Errors:
			log.Printf("Error: NewWatcher: MonitorEvents: %v\n", err)
		case <-c.exitChan:
			return
		}
	}
}

// // regLoop 文件监控注册事件
// func (c *WatchManager) regLoop() {
//     defer func() {
//         log.Printf("DEBUG: fsnotifyutil: regLoop stop.\n")
//         if err := recover(); err != nil {
//             log.Printf("Error: fsnotifyutil: regLoop: err:%v\n", err)
//         }
//     }()
//
//     for {
//         select {
//         case wf := <-c.fileChan:
//             log.Printf("DEBUG: file:%v wait to monitor.", wf)
//
//             if err := c.watcher.Add(wf.GetFileName()); err != nil {
//                 log.Printf("Error: watcher.Add:%v %v\n", wf.GetFileName(), err)
//             }
//         case <-c.exitChan:
//             return
//         }
//     }
// }

func (c *WatchManager) Register(fileName string, cb func(string) error) {
	wf := &WatchFile{
		f:        fileName,
		callback: cb,
	}

	// 初始化加载
	if err := cb(fileName); err != nil {
		log.Printf("Error: fsnotifyutil: Register.Init: err:%v\n", err)
	}

	// 注册监控事件
	if err := c.watcher.Add(wf.GetFileName()); err != nil {
		log.Printf("Error: watcher.Add:%v %v\n", wf.GetFileName(), err)
	}

	// 设置文件监控表
	c.setWatchTable(fileName, wf)
}

func (c *WatchManager) setWatchTable(fileName string, wf *WatchFile) {
	c.Lock()
	defer c.Unlock()
	c.watchTable[fileName] = wf
}

func (c *WatchManager) getWatchFile(fileName string) (*WatchFile, bool) {
	c.Lock()
	defer c.Unlock()
	wf, ok := c.watchTable[fileName]
	return wf, ok
}

// Close 服务器主程序退出调用通知回调函数
func (c *WatchManager) Close() {
	log.Printf("DEBUG: fsnotifyutil: receive close.\n")
	c.closeOnce.Do(func() {
		close(c.exitChan)
		c.watcher.Close()
	})
}
